package com.hula.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

import java.lang.reflect.Field;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * @author liuhe
 * @date 2021/3/24
 * @Description RSA加解密
 */
public class RSASecretHelper {
    public static final String KEY_ALGORITHM = "RSA";
    private static final String SHA_256_WITH_RSA = "SHA256WithRSA";

    /**
     * 使用私钥验签
     * @param sign 签名
     * @param jsonObject 需加密数据
     * @param base64PrivateKeyStr 私钥
     * @return true-验签成功， false-验签失败
     */
    public static boolean checkSignByPrivateKey(String sign, JSONObject jsonObject, String base64PrivateKeyStr) {
        String jsonObjectSortStr = RSASecretHelper.getJsonObjectSortStr(jsonObject);
        final String serverSign = encryptByPrivateKey(jsonObjectSortStr.getBytes(), base64PrivateKeyStr);
        return serverSign.equals(sign);
    }

    /**
     * 使用私钥加密
     * @param jsonObject 需加密数据
     * @param base64PrivateKeyStr 私钥
     * @return 签名
     */
    public static String encryptByPrivateKey(JSONObject jsonObject, String base64PrivateKeyStr) {
        String jsonObjectSortStr = RSASecretHelper.getJsonObjectSortStr(jsonObject);
        return encryptByPrivateKey(jsonObjectSortStr.getBytes(), base64PrivateKeyStr);
    }

    /**
     * 使用私钥加密
     * @param content 需加密数据
     * @param base64PrivateKeyStr 私钥
     * @return 签名
     */
    public static String encryptByPrivateKey(byte[] content, String base64PrivateKeyStr) {
        // 对密钥解密
        byte[] privateKeyBytes = Base64.getDecoder().decode(base64PrivateKeyStr);

        // 取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

            // 对数据加密
            Signature signature = Signature.getInstance(SHA_256_WITH_RSA);
            signature.initSign(privateKey);
            signature.update(content);//data 为要生成签名的源数据字节数组
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            throw new RuntimeException("数据加密失败:" + e.getMessage());
        }
    }

    /**
     * 使用公钥验签
     * @param sign 签名
     * @param jsonObject 需加密数据
     * @param base64PublicKeyStr 公钥
     * @return true-验签成功， false-验签失败
     */
    public static boolean decryptByPublicKey(String sign, JSONObject jsonObject, String base64PublicKeyStr) {
        String jsonObjectSortStr = RSASecretHelper.getJsonObjectSortStr(jsonObject);
        return decryptByPublicKey(sign, jsonObjectSortStr.getBytes(), base64PublicKeyStr);
    }

    /**
     * 使用公钥验签
     * @param sign 签名
     * @param content 需加密数据
     * @param base64PublicKeyStr 公钥
     * @return true-验签成功， false-验签失败
     */
    public static boolean decryptByPublicKey(String sign, byte[] content, String base64PublicKeyStr) {
        // 对密钥解密
        byte[] publicKeyBytes = Base64.getDecoder().decode(base64PublicKeyStr);
        byte[] signByte = Base64.getDecoder().decode(sign);//签名需要做 base64 解码
        try {
            // 取得公钥
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);

            // 对数据解密
            Signature signature = Signature.getInstance(SHA_256_WITH_RSA);
            signature.initVerify(publicKey);
            signature.update(content);
            return signature.verify(signByte);
        } catch (Exception e) {
            throw new RuntimeException("数据解密失败:" + e.getMessage());
        }
    }

    /**
     * 获取排序的结果
     */
    public static String getObjectSortStr(Object object) {
        //解析为非空map
        Map<String, String> map = objectToMap(object);
        //排序
        return MapToAsciiString(map);
    }

    /**
     * 获取排序的结果(对jsonObject解析)
     */
    public static String getJsonObjectSortStr(JSONObject object) {
        //解析为非空map
        Map<String, String> map = jsonObjectToMap(object);
        //排序
        return MapToAsciiString(map);
    }

    /**
     * 将 map 中参数按照 ASCII 码排序
     */
    private static String MapToAsciiString(Map<String, String> map) {
        Collection<String> keySet = map.keySet();
        List<String> list = new ArrayList<>(keySet);
        Collections.sort(list);
        StringBuilder sb = new StringBuilder();
        int i, len;
        len = list.size();
        for (i = 0; i < len; i++) {
            sb.append(list.get(i))
                    .append("=")
                    .append(map.get(list.get(i)))
                    .append("&");
        }
        return sb.length() > 0 ? sb.substring(0, sb.length() - 1) : sb.toString();
    }

    /**
     * 对象转 map
     */
    private static Map<String, String> objectToMap(Object obj) {
        if (obj == null) {
            return Collections.emptyMap();
        }
        Map<String, String> map = new HashMap<>();
        try {
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if (field.get(obj) != null && !"".equals(field.get(obj).toString())
                        && !"serialVersionUID".equals(field.getName()) && !"sign".equals(field.getName())) {
                    if (field.getType().isInterface()) {
                        map.put(field.getName(), JSON.toJSONString(field.get(obj)));
                    } else {
                        map.put(field.getName(), field.get(obj).toString());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * jsonObject对象转 map
     */
    private static Map<String, String> jsonObjectToMap(JSONObject obj) {
        if (obj == null) {
            return Collections.emptyMap();
        }
        Map<String, String> map = new HashMap<>();
        try {
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                final Object fieldValue = field.get(obj);
                final String fieldName = field.getName();
                if (excludeField(fieldValue, fieldName)) {
                    if (field.getType().isInterface()) {
                        //json默认字段
                        if ("map".equals(fieldName) && (fieldValue instanceof Map)) {
                            Map<String, Object> subMap = (Map<String, Object>) fieldValue;
                            for (Map.Entry<String, Object> entry : subMap.entrySet()) {
                                if (excludeField(entry.getKey()) && entry.getValue() != null) {
                                    if (entry.getValue() instanceof JSONArray){
                                        JSONArray array = (JSONArray)entry.getValue();
                                        if (array.size() > 0 && array.get(0) instanceof JSONObject) {
                                            List<TreeMap<String, Object>> list = JSONObject.parseObject(entry.getValue().toString(), new TypeReference<List<TreeMap<String, Object>>>() {});
                                            map.put(entry.getKey(), JSON.toJSONString(list));
                                        }else {
                                            map.put(entry.getKey(), entry.getValue().toString());
                                        }
                                    }else {
                                        map.put(entry.getKey(), entry.getValue().toString());
                                    }
                                }
                            }
                        }else {
                            map.put(fieldName, JSON.toJSONString(fieldValue));
                        }
                    } else {
                        map.put(fieldName, fieldValue.toString());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 排除部分解析的字段
     * @param fieldValue 字段值
     * @param fieldName 字段名
     * @return
     */
    public static boolean excludeField(Object fieldValue, String fieldName) {
        return fieldValue != null && !"".equals(fieldValue.toString())
                && !"serialVersionUID".equals(fieldName)
                && !"sign".equals(fieldName)
                && !"DEFAULT_INITIAL_CAPACITY".equals(fieldName);
    }

    /**
     * 排除部分解析的字段
     */
    public static boolean excludeField(String fieldName) {
        return !"sign".equals(fieldName) && !"publicKey".equals(fieldName);
    }

}
